package org.nico.noson.util.type;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.nico.noson.NosonConfig;
import org.nico.noson.adapter.type.AbstractTypeAdapter;
import org.nico.noson.cache.Cache;
import org.nico.noson.cache.CacheManager;
import org.nico.noson.cache.SimpleCache;
import org.nico.noson.entity.NoType;
import org.nico.noson.entity.TypeBean;
import org.nico.noson.exception.NosonException;
import org.nico.noson.scanner.impl.SimpleScanner.SimpleStruct;
import org.nico.noson.scanner.plant.AbstractPlant;
import org.nico.noson.util.asm.ASMClassProxyBuilder;
import org.nico.noson.util.asm.AbstractASMClassProxy;
import org.nico.noson.util.reflect.FieldUtils;
import org.nico.noson.util.string.FormatUtils;

/** 
 * Noson类型转换工具
 * 
 * @author nico
 * @version 创建时间：2017年11月25日 下午4:55:08
 */
public class TypeUtils {

	public static final String BOOLEAN_TRUE = "TRUE";

	public static final String BOOLEAN_FALSE = "FALSE";

	public static ASMClassProxyBuilder asmClassProxyBuilder = new ASMClassProxyBuilder();
	
	/**
	 * 数字类型集合
	 */
	public static final Set<Class<?>> NUM_CLASS_SET = new HashSet<Class<?>>(){
		private static final long serialVersionUID = -1925389609848043520L;
		{
			add(int.class);
			add(short.class);
			add(double.class);
			add(long.class);
			add(float.class);
			add(byte.class);
		}
	};

	/**
	 * 转换Json字符串中的Value
	 * 
	 * @param param
	 * @return
	 */
	public static Object typeAllotValue(String param){
		if(param == null)
			return param;
		Object result = param;
		if((result = CacheManager.getValueCache().getCache(param)) != null){
			return result;
		}else{
			try{
				result = NosonConfig.DEFAULT_DATE_FORMAT.parse(param);
			}catch(ParseException e0){
				try{
					result = Double.parseDouble(param);
				}catch(NumberFormatException e3){
					try{
						result = new BigDecimal(param);
					}catch(NumberFormatException e4){
						if(BOOLEAN_TRUE.equalsIgnoreCase(param) || BOOLEAN_FALSE.equalsIgnoreCase(param)){
							result = Boolean.parseBoolean(param);
						}else{
							if(param.equals("null")){
								result = null;
							}else{
								result = param;
							}
						}
					}
				}
			}
		}
		result = result instanceof String ? FormatUtils.deEscape((String) result) : result;
		CacheManager.getValueCache().putCache(param, result);
		return result;
	}

	/**
	 * 转换Json中的key
	 * 
	 * @param param
	 * @return
	 */
	public static String typeAllotKey(String param){
		if(param == null)
			return param;
		String result = param;
		if(CacheManager.getKeyCache().containsCache(param)){
			return String.valueOf(CacheManager.getKeyCache().getCache(param));
		}
		result = result instanceof String ? FormatUtils.deEscape((String) result) : result;
		CacheManager.getKeyCache().putCache(param, result);
		return result;
	}

	/**
	 * 判断是否是基本类型
	 * @param clazz 被判断的class
	 * @return 验证结果
	 */
	public static boolean isInseparable(Class<?> clazz){
		boolean inseparable = false;
		if(clazz.isPrimitive()){
			inseparable = true;
		}else if(Number.class.isAssignableFrom(clazz)){
			inseparable = true;
		}else if(clazz.isAssignableFrom(String.class)){
			inseparable = true;
		}else if(clazz.isAssignableFrom(Boolean.class) || clazz.equals(double.class)){
			inseparable = true;
		}else if(Date.class.isAssignableFrom(clazz)){
			inseparable = true;
		}else if(Enum.class.isAssignableFrom(clazz)){
			inseparable = true;
		}
		return inseparable;
	}

	/**
	 * 对被转换的对象进行包装
	 * 
	 * @param obj 被转换的对象
	 * @return 包装后的字符串
	 */
	public static String typeWrap(Class<?> type, Object obj){
		if(obj == null) return emptyWrap(type, NosonConfig.ALLOW_EMPTY_TO_NULL);
		if(isInseparable(obj.getClass())){
			if(obj instanceof String){
				return "\"" + FormatUtils.enEscape((String) obj) + "\"";
			}else if(obj instanceof Date){
				return "\"" + NosonConfig.DEFAULT_DATE_FORMAT.format((Date)obj) + "\"";
			}else if(obj instanceof Enum){
				return "\"" + obj.toString() + "\"";
			}else{
				return obj.toString();
			}
		}
		return "\"" + obj.getClass().getName() + "\"";
	}

	/**
	 * If obj is null. this method can help noson finished convert
	 * 
	 * @param type	Obj's type
	 * @param toNull Whether object is set to null.
	 * @return result
	 */
	public static String emptyWrap(Class<?> type, boolean toNull){
		String result = null;
		if(Object.class.isAssignableFrom(type)){
			if(! toNull){
				if(Number.class.isAssignableFrom(type)){
					result = "0";
				}else if(String.class.isAssignableFrom(type)){
					result = "\"\"";
				}
			}
		}else{
			if(type.isAssignableFrom(int.class)){
				result = "0";
			}else if(type.isAssignableFrom(float.class)){
				result = "0.000";
			}else if(type.isAssignableFrom(double.class)){
				result = "0.000000";
			}else if(type.isAssignableFrom(long.class)){
				result = "0";
			}else if(type.isAssignableFrom(short.class)){
				result = "0";
			}else if(type.isAssignableFrom(byte.class)){
				result = "0";
			}else if(type.isAssignableFrom(boolean.class)){
				result = "false";
			}
		}
		return result;
	}

	/**
	 * 获取一个表示结构类型（List, Map, Object）的实例
	 * 
	 * @param clazz 
	 * @return
	 * @throws NosonException
	 */
	public static Object getStructInstance(Class<?> clazz) throws NosonException{
		Object target = AbstractPlant.TYPE_PLANT.get(clazz);
		if(target == null){
			if(clazz.isArray()){
				target = new LinkedList<Object>();
			}else{
				try {
					target = clazz.newInstance();
				} catch (Exception e) {
					throw new NosonException(e.getMessage(), e);
				}
			}
		}else{
			target = ((AbstractPlant)target).get();
		}
		return target;
	}

	/**
	 * 将param插入到target中
	 * 
	 * @param target 
	 * 			目标对象
	 * @param key
	 * 			注入参数名
	 * @param value 
	 * 			注入参数
	 * @throws NosonException
	 */
	public static void setParamIntoObject(Object target, SimpleStruct currentStruct) throws NosonException{
		String key = currentStruct.getName();
		Object value = currentStruct.getValue();
		//对类型为array的特殊处理
		if(currentStruct.getClassType() != null 
				&& currentStruct.getValue() != null 
				&& currentStruct.getClassType().isArray()){
			Collection list = (Collection) value;
			Class<?> componentType = currentStruct.getClassType().getComponentType();
			Object array = Array.newInstance(componentType, list.size());
			int index = 0;
			for(Object obj: list){
				Array.set(array, index++, convertType(componentType, obj));
			}
			value = array;
		}
		if(target instanceof Collection){
			((Collection<Object>) target).add(value);
		}else if(target instanceof Map){
			((Map) target).put(key, value);
		}else if(target.getClass().isArray()){
			target = getNewArray(target, target.getClass().getComponentType(), value);
		}else{
			AbstractASMClassProxy asmClassProxy = null;
			if((asmClassProxy = (AbstractASMClassProxy) CacheManager.getClassProxyCache().getCache(target.getClass().getName())) == null){
				asmClassProxy = asmClassProxyBuilder.getASMClassProxy(target.getClass());
				CacheManager.getClassProxyCache().putCache(target.getClass().getName(), asmClassProxy);
			}
			Class<?> type = getFieldType(target.getClass(), key);
			
			value = convertType(type, value);
			
			asmClassProxy.set(target, key, value);
		}
	}
	
	/**
	 * Get {@link Field} type from clazz by field name with cache.
	 * 
	 * @param clazz 
	 * 			Target class
	 * @param fieldName 
	 * 			Field name
	 * @return
	 * 			Field Type
	 * @throws NosonException
	 */
	public static Class<?> getFieldType(Class<?> clazz, String fieldName) throws NosonException{
		Class<?> type = null;
		String fieldCacheKey = clazz.getName() + fieldName;
		if((type = (Class<?>) CacheManager.getFieldTypeCache().getCache(fieldCacheKey)) == null){
			Field field = FieldUtils.getField(fieldName, clazz);
			type = field.getType();
			CacheManager.getFieldTypeCache().putCache(fieldCacheKey, type);
		}
		return type;
	}

	public static <T> Object getNewArray(Object array, Class<T> componentType, Object value) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, NosonException{
		int len = Array.getLength(array);
		Object newArray = Array.newInstance(componentType, len + 1);
		System.arraycopy(array, 0, newArray, 0, len);
		Array.set(newArray, len, convertType(componentType, value));
		return newArray;
	}

	/**
	 * 对要解析的类型进行泛型迭代，将主要转换类型存放在一个数组里面，
	 * 主要提供与Convert阶段有序的类型匹配
	 * 
	 * @param clazz 要解析的类型
	 * @return 类型及其泛型迭代
	 * @throws NosonException
	 */
	public static List<Class<?>> getGenericityTypeIterate(Class<?> clazz){
		List<Class<?>> typeList = new ArrayList<Class<?>>();
		if(clazz != null){
			TypeBean<?> header = getGenericityType(clazz);
			/**
			 * 如果是NoType类型，则跳过其本身处理，获取其第一个泛型处理！
			 */
			if(NoType.class.isAssignableFrom(header.getMainClass())){
				header = header.getGenericityBeans()[0];
			}
			typeList.add(header.getMainClass());
			TypeBean<?> currentType = header;
			while(currentType != null && currentType.getGenericityBeans() != null){
				TypeBean<?>[] typeBeans = currentType.getGenericityBeans();
				switch(typeBeans.length){
				case 0:
					typeList.add(Object.class);
					currentType = null;
					break;
				case 1:
					currentType = typeBeans[0];
					typeList.add(currentType.getMainClass());
					break;
				case 2:
					currentType = typeBeans[1];
					typeList.add(currentType.getMainClass());
					break;
				default:
					throw new RuntimeException("Exceeding the limit of the generic length !!");
				}
			}
		}
		return typeList;
	}

	/**
	 * 对要解析的类型进行泛型迭代，并打包成TypeBean树形结构。
	 * 
	 * @param clazz
	 * @return
	 */
	public static TypeBean<?> getGenericityType(Class<?> clazz){
		TypeBean<?> typeBean = new TypeBean(clazz);
		if(clazz == null)
			throw new NullPointerException("Class is null");
		Type superclass = clazz.getGenericSuperclass();
		if(superclass != null){
			if(superclass instanceof Class) {
				//				throw new RuntimeException("Missing type parameter.");
			} else {
				ParameterizedType parameterized = (ParameterizedType)superclass;
				if(! Modifier.isPublic(clazz.getModifiers())){
					typeBean.setMainClass((Class)parameterized.getRawType());
				}
				Type[] types = typesWrapper(clazz, parameterized.getActualTypeArguments());
				typeBean.setGenericityBeans(getGenericityTypes(types));
			}
		}
		return typeBean;
	}

	public static Type[] typesWrapper(Class<?> clazz, Type[] types){
		int len = 1;
		if(Map.class.isAssignableFrom(clazz)){
			len = 2;
		}else if(Collection.class.isAssignableFrom(clazz)){
			len = 1;
		}
		if(types == null){
			types = new Type[]{String.class, Object.class};
		}else if(types.length != len){
			Type[] newTypes = new Type[len];
			for(int index = 0; index < newTypes.length; index ++){
				if(index < types.length){
					newTypes[index] = types[index];
				}else{
					newTypes[index] = Object.class;
				}
			}
		}
		return types;
	}

	/**
	 * 获取Class的泛型类型数组
	 * 
	 * @param clazz class
	 * @return
	 */
	public static TypeBean<?>[] getGenericityTypes(Type[] types){
		if(types.length == 0)
			throw new ArrayIndexOutOfBoundsException(0);
		TypeBean<?>[] typeBeans = new TypeBean[types.length];
		int index = 0;
		for(Type type: types){
			TypeBean<?> typeBean = null;
			if(type instanceof ParameterizedType){
				Class<?> targetClass = (Class<?>)((ParameterizedType) type).getRawType();
				typeBean = new TypeBean(targetClass);
				typeBean.setGenericityBeans(getGenericityTypes(typesWrapper(targetClass, ((ParameterizedType) type).getActualTypeArguments())));
			}else{
				typeBean = new TypeBean((Class<?>)type);
			}
			typeBeans[index ++] = typeBean;
		}
		return typeBeans;
	}

	/**
	 * 将target转为clazz的类型对象
	 * 
	 * @param clazz 要转换的目标对象类型
	 * @param target 待转对象
	 * @return 转换后的对象
	 * @throws NosonException 
	 * @throws ParseException 
	 */
	public static Object convertType(Class<?> clazz, Object target) throws NosonException{
		if(target == null && clazz == null){
			return null;
		}
		if(target == null && clazz != null){
			return emptyWrap(clazz, NosonConfig.ALLOW_EMPTY_TO_NULL);
		}
		
		Class<?> key = clazz;
		
		if(Enum.class.isAssignableFrom(clazz)){
			key = Enum.class;
		}
		AbstractTypeAdapter typeAdapter = null;
		if( null != (typeAdapter = AbstractTypeAdapter.TYPE_ADAPTER_MAP.get(key))){
			return typeAdapter.typeAdapter(clazz, target);
		}else{
			return target;
		}
	}

}
